package inabit.data;

import inabit.impl.data.types.GenericType;
import inabit.utils.CompareStrategy;
/**Represents an atom, contains an id and a value
 * @author Rafael Rocha*/
public abstract class Variable implements Comparable<Variable>{
	private String id;
	private String value;
	private String savedValue;
	private VariableInfo info;
	private VariableInfo savedInfo;
	private CompareStrategy<Variable> cs;
	private VariableType type;
	
	/**Creates a variable with an specific type
	 * @see inabit.data.VariableType*/
	public Variable(String id,VariableType type) {
		this.id = id;
		
		cs = new VariableNameCompareStrategy();
		this.type = type;
		this.info = new VariableInfo();
		this.savedInfo = info.copy();
		save();
	}
	/**Creates a variable with a generic type, which means
	 * it can assume any value*/
	public Variable(String id) {
		this(id, new GenericType());
	}
	
	/**CompareStategy is used so that the variables can be ordered
	 * at different ways, by default the variables are ordered in alphanumeric
	 * order of theirs ids*/
	public void setCompareStrategy(CompareStrategy<Variable> cs){
		this.cs = cs;
	}
	/**Returns the comparing strategy implemented by this rule*/
	public CompareStrategy<Variable> getCompareStrategy() {
		return cs;
	}
	public int compareTo(Variable target){
		return cs.compare(this, target);
	}
	
	public String getID() {
		return id;
	}
	/**Returns information about the variable like the values it can't assume
	 * the minimum and maximal value and etc...
	 * @see inabit.data.VariableInfo*/
	public VariableInfo getInfo() {
		return info;
	}
	/**Treats the string by it's type and retuns value*/
	protected String getVarValue(String value){
		return type.getVarValue(value);
	}
	
	/**Set the variable value if it's valid*/
	public void setValue(String value){
		if(isValidValue(value)){
			this.value = getVarValue(value);
		}
	}
	/**Returns the variable value*/
	public String getValue() {
		return value;
	}
	
	/**Returns true if the value passed by parameter is acceptable to the variable*/
	public boolean isValidValue(String value) {
		return isAcceptableValue(value) && info.isValid(value);
	}
	/**Returns true if the value passed by parameter is acceptable by the type of the variable*/
	protected boolean isAcceptableValue(String value) {
		return type.isAcceptableValue(value);
	}
	/**Changes the information about the variable
	 * @see inabit.data.VariableInfo*/
	public void setInfo(VariableInfo variableInfo) {
		this.info = variableInfo;
	}
	/**Save the current state of the variable so that if it gets restored this will be
	 * state the variable will return*/
	public void save() {
		this.savedInfo = info.copy();
		this.savedValue = value;
	}
	/**Restore the variable to its last saved state, if the method <i>save</i> was not called
	 * then the variable value is set to null and information about its state is erased*/
	public void restore() {
		this.value = savedValue;
		this.info = savedInfo.copy();
	}
	
	/**Returns true if the type of variable is numeric*/
	public boolean isNumeric() {
		return type.isNumeric();
	}
	/**Returs the variable type
	 * @see inabit.data.VariableType*/
	public VariableType getType() {
		return type;
	}

	/**Try to set the value of the variable based on the information about its state*/
	public abstract void tryAssignValue();
}
